//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"
#undef bind

//1 dw for the client id of the registry
//1 for the size of the event and the registry interface event opcode
//the others are arguments for the global event:
//  o the global server "name"
//  o the interface string:
//    - the interface string size, including the null terminating char
//    - the interface string padded to dw, worst case scenario
//  o the interface version
#define EV_GLOBAL_DWS_MAX (1+1+1+(1+WL_ITFS_STRS_DWS_MAX)+1)
s8 wl_registry_ev_global(struct client *c,u32 id,s32 name)
{
  //build the event payload
  u32 ev[EV_GLOBAL_DWS_MAX];

  ev[0]=id;
  u16 itf_cstr_dws=wl_itfs_strs_dws[registry[name].itf];
  u16 itf_cstr_sz=wl_itfs_strs_sz[registry[name].itf];
  u16 ev_sz=(1+1+1+(1+itf_cstr_dws)+1)*sizeof(u32);
  ev[1]=ev_sz<<16|WL_REGISTRY_GLOBAL;
  ev[2]=name;//actual object server name
  ev[3]=itf_cstr_sz;
  strncpy(&ev[4],wl_itfs_strs[registry[name].itf],itf_cstr_dws*sizeof(u32));
  memset((u8*)&ev[4]+itf_cstr_sz,0,itf_cstr_dws*sizeof(u32)-itf_cstr_sz);
  ev[4+itf_cstr_dws]=1;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):registry::global name=%u itf=%s\n",c->so,name,
                                              wl_itfs_strs[registry[name].itf]);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):registry::global unable to send event\n",c->so,
                                                                             r);
    r=LWL_ERR;goto exit;
  }
  if(r!=ev_sz){
    PERR("client(%d):fatal:registry::global only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}

#define EV_GLOBAL_REMOVE_DWS 3
s8 wl_registry_ev_global_remove(struct client *c,u32 id,u32 name)
{
  u32 ev[EV_GLOBAL_REMOVE_DWS];

  ev[0]=id;
  u16 ev_sz=EV_GLOBAL_REMOVE_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_REGISTRY_GLOBAL_REMOVE;
  ev[2]=name;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):registry::global_remove name=%u\n",c->so,name);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):registry:global_remove unable to send event\n",
                                                                       c->so,r);
    return LWL_ERR;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:registry::global_remove event only %ld/%ld were"
                                                       " sent\n",c->so,r,ev_sz);
    return LWL_ERR;
  }
  return LWL_OK; 
}

static s8 bind(struct client *c,struct msg *m)
{
  s8 r;
  if(m->req_dws<4){
    PERR("client(%d):fatal:registry::bind request missing interface name"
                                                             " length\n",c->so);
    r=LWL_ERR;goto exit;
  }

  s32 name_to_bind=(s32)m->req[2];

  u16 itf_name_dws=DWS_ROUNDUP(m->req[3]);
  u16 expected_dws=1+1+1+(1+itf_name_dws)+1+1;
  if(m->req_dws<expected_dws){
    PERR("client(%d):fatal:registry::bind request missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }
  u32 id=m->req[4+itf_name_dws+1];

  LOG_WIRE("client(%d):registry::bind name=%d id=%u\n",c->so,name_to_bind,id);

  r=registry_bind(c,name_to_bind,id);

exit:
  return r;
}

s8 wl_registry_req(struct client *c,struct msg *m)
{
  s8 r;
  switch(m->req_op){
  case WL_REGISTRY_BIND:
    r=bind(c,m);
    break;
  default:
    PERR("client(%d):fatal:registry unknown opcode %u\n",c->so,m->req_op);
    r=LWL_ERR;
  }
  return r;
}
